/***************************************************************
*
* (c) 2008 Markus Dittrich 
*
* This program is free software; you can redistribute it 
* and/or modify it under the terms of the GNU General Public 
* License Version 3 as published by the Free Software Foundation. 
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License Version 3 for more details.
*
* You should have received a copy of the GNU General Public 
* License along with this program; if not, write to the Free 
* Software Foundation, Inc., 59 Temple Place - Suite 330, 
* Boston, MA 02111-1307, USA.
*
****************************************************************/

/** boost headers */
#include <boost/filesystem/path.hpp>
#include <boost/scoped_ptr.hpp>

/** STL headers */
#include <iostream>
#include <algorithm>

/** Qt headers */
#include <QAction>
#include <QColor>
#include <QComboBox>
#include <QDir>
#include <QFileDialog>
#include <QFileInfo>
#include <QGridLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QIcon>
#include <QLabel>
#include <QListWidget>
#include <QListWidgetItem>
#include <QMenuBar>
#include <QMenu>
#include <QMessageBox>
#include <QMutexLocker>
#include <QPrinter>
#include <QPrintDialog>
#include <QProgressBar>
#include <QPushButton>
#include <QRadioButton>
#include <QSplitter>
#include <QStackedWidget>
#include <QStatusBar>
#include <QTabWidget>
#include <QTabBar>
#include <QToolBar>
#include <QToolButton>

/** local headers */
#include "about_dvalin.h"
#include "data_compute_engine.h"
#include "data_label_widget.h"
#include "data_selector_widget.h"
#include "graph_canvas.h"
#include "histogram_selector_widget.h"
#include "main_window.h"
#include "plot_properties.h"
#include "property_widgets.h"
#include "settings_widget.h"

/** pull in a few things from std namespace */
using std::vector;
using std::string;
using std::map;


/**************************************************************
 *
 * PUBLIC FUNCTIONS 
 *
 **************************************************************/

//------------------------------------------------------------
// constructor
//------------------------------------------------------------
MainWindow::MainWindow(const vector<QString>& fileNames) 
  :
    histogramCounter_(0)
{
  setWindowTitle(tr("dvalin"));

  generate_ioHandler_();
  create_canvas_();
  create_menu_bar_();
  create_tool_bar_();
  populate_menu_bar_();
  create_main_splitter_();
  create_data_property_widget_();
  create_data_selector_widget_();
  create_status_bar_();
  add_widgets_to_splitter_();

  setCentralWidget(mainSplitter_);

  /* retrieve data from files provided on command line if any
   * then display and plot */ 
  if ( !fileNames.empty() )
  {
    ioHandler_->append_to_filequeue(fileNames);
    ioHandler_->start();
  }

  /* connect main window's signals to components */
  connect(this, SIGNAL(graph_color_changed(const QString&, 
    const QColor&)), dataSelector_, 
    SLOT(change_label_color(const QString&, const QColor&)));
}


/**************************************************************
 *
 * PUBLIC SLOTS
 *
 *************************************************************/

//-------------------------------------------------------------
// list the currently processed file in the status bar 
//-------------------------------------------------------------
void MainWindow::list_in_status_bar(QString fileName)
{
  QString message(tr("currently loading <b>") + fileName 
      + tr("</b>"));
  statusBarMessages_->setText(message);
}


//------------------------------------------------------------
// open QMessage box to signal that reading of a file
// failed 
// NOTE: Provide better description of what exactly went
// wrong if possible
//------------------------------------------------------------
void MainWindow::signal_file_reading_error(QString fileName)
{
  QMessageBox::critical(this, tr("Error"), 
    tr("Failed to parse file ") + fileName,
    QMessageBox::Ok);
   
  return;
}
  
  
//------------------------------------------------------------
// force a complete re-draw of the canvas including rescaling
// if neccesary, e.g. after reloading or a change in
// displayed data column
//------------------------------------------------------------
void MainWindow::redraw_and_rescale_canvas() 
{
  canvas_->mark_scaling_dirty();
  canvas_->update_pixmap();
}


//------------------------------------------------------------
// force a complete re-draw of the canvas without rescaling
//------------------------------------------------------------
void MainWindow::redraw_canvas() 
{
  canvas_->update_pixmap();
}


//-------------------------------------------------------------
// updates the mouse position display triggered by signals
// from GraphCanvas
//-------------------------------------------------------------
void MainWindow::update_mouse_position(double xPos, double yPos)
{
  QString newLabel = generate_coordinate_string(xPos, yPos);
  currentCoordinateWidget_->setText(newLabel); 
}


//-------------------------------------------------------------
// hides selected data sets
//-------------------------------------------------------------
void MainWindow::hide_data_items(bool state,
    QList<QString> hiddenItems)
{
  /* return if nothing to do */
  if ( hiddenItems.empty() )
  {
    return;
  }

  FOREACH(const QString& name, hiddenItems)
  {
    PlotPropertiesPtr plotProperties = plotPropertiesContainer_[name];
    plotProperties->set_visibility(state);
  }

  redraw_and_rescale_canvas();
}


//-------------------------------------------------------------
// deletes selected data sets
//-------------------------------------------------------------
void MainWindow::delete_data_items(QList<QString> deadItems)
{
  /* loop over all selected data sets and remove them */
  FOREACH(const QString& deadDataName, deadItems)
  {
    BasePropertyWidget* deadWidget = 
      get_data_property_widget_(deadDataName);

    /* something went wrong */
    if ( deadWidget == NULL )
    {
      QMessageBox::critical(this, tr("Error"), 
        tr("Failed to delete data set ") + deadDataName,
        QMessageBox::Ok);
      return;
    }

    /* remove property widget and entry in data selector */
    dataControl_->removeWidget(deadWidget); 
    deadWidget->deleteLater();

    /* remove data from containers and filequeue */
    dataContainer_.erase(deadDataName);
    plotPropertiesContainer_.erase(deadDataName);
    remove_from_active_data_(deadDataName); 
    remove_from_data_label_manager_(deadDataName);
    ioHandler_->remove_from_filequeue(deadDataName);
  }

  redraw_and_rescale_canvas();
}


//-------------------------------------------------------------
// slot in charge of generating histogram instances for 
// presently selected data sets
//-------------------------------------------------------------
void MainWindow::generate_histogram(int numBins, bool normalize)
{
  /* generate histogram compute engine */
  boost::scoped_ptr<HistogramComputeEngine> 
    histogram(new HistogramComputeEngine(numBins, normalize));
  
  /* loop over all presently selected data sets */
  QList<QString> selectedDataNames = 
    dataSelector_->get_selected_item_names();
  FOREACH(const QString& dataName, selectedDataNames)
  {
    /* retrieve y-data and pass it on to the histogram
     * compute engine */
   DataObjectPtr selectedData = dataContainer_[dataName];   
   DataObjectPtr histogramData = 
     histogram->compute(selectedData->get_y_data());

   /* update gui and widgets with new data */
   add_histogram_(dataName, histogramData, numBins, normalize);
  }
}


//-------------------------------------------------------------
// slot in charge of updating an already existing histogram,
// e.g., upon a change in the number of bins.
//
// NOTE: We need to catch the case in which the user has
// already deleted the dataset underlying the histogram
// in which case we can't recompute any more.
//-------------------------------------------------------------
void MainWindow::update_histogram(const QString& dataName, 
    const QString& histogramName, int numBins, bool normalize)
{
  /* generate histogram compute engine */
  boost::scoped_ptr<HistogramComputeEngine> 
    histogram(new HistogramComputeEngine(numBins, normalize));

  /* retrieve data if still present and recompute histogram */
  dataMapIterator dataLoc = dataContainer_.find(dataName);
  if ( dataLoc == dataContainer_.end() )
  {
    QMessageBox::critical(this, tr("Error"), 
      tr("Could not find the data set underlying histogram!\n") 
      + tr("Did you delete it?"), QMessageBox::Ok);
    return;
  }

  DataObjectPtr selectedData = dataContainer_[dataName];   
  DataObjectPtr histogramData = 
     histogram->compute(selectedData->get_y_data());

  /* replace existing histogram data */
  dataContainer_[histogramName] = histogramData;
  update_individual_property_tab_(histogramName);

  redraw_and_rescale_canvas();
}


//-------------------------------------------------------------
// prepare and add currently selected data sets as 
// histograms 
// NOTE: We have to check that
// the user has at least one data set selected
//-------------------------------------------------------------
void MainWindow::show_histogram_selector()
{
  /* make sure at least one data set was selected */
  if ( dataSelector_->num_selected_items() <= 0 )
  {
    QMessageBox::critical(this, tr("Error"), 
      tr("Please select at least on data set!"), QMessageBox::Ok);
    return;
  }

  HistogramSelectorWidget* histogramSelector = 
    new HistogramSelectorWidget(this);
  histogramSelector->Init();
  histogramSelector->setAttribute(Qt::WA_DeleteOnClose);
  histogramSelector->show();
}


 
/**************************************************************
 *
 * PRIVATE SLOTS
 *
 *************************************************************/

//-------------------------------------------------------------
// slot responsible for displaying a window with a short info
// and copyright notice
//-------------------------------------------------------------
void MainWindow::show_info_()
{
  AboutDvalinWidget aboutDvalin;
  aboutDvalin.exec();
}


//-------------------------------------------------------------
// slot responsible for displaying a the default Qt info
// widget
//-------------------------------------------------------------
void MainWindow::show_about_qt_info_()
{
  QMessageBox::aboutQt(this,tr("About Qt"));
}


//-------------------------------------------------------------
// slot responsible for opening a file dialog to load a new
// data file
//-------------------------------------------------------------
void MainWindow::show_open_new_file_widget_()
{
  /* make sure no other IO thread is already running */
  if ( ioHandler_->isRunning() )
  {
    QMessageBox::warning(this, tr("Warning"), 
      tr("Please wait for current IO thread to finish"),
      QMessageBox::Ok);
    return;
  }

  QString currentDirectory = QDir::currentPath();
  QString fileName = QFileDialog::getOpenFileName(this,
      tr("open data file"), currentDirectory,
      tr("data files (*.dat)"));

  /* append to our file queue and read it */
  ioHandler_->append_to_filequeue(fileName);
  ioHandler_->start();
}


//-------------------------------------------------------------
// slot responsible for printing the current canvas 
//-------------------------------------------------------------
void MainWindow::show_print_canvas_widget_()
{
  /* create printer and fire up print dialog */
  QPrinter aPrinter;
  QPrintDialog printDialog(&aPrinter, this);
  if ( printDialog.exec() == QDialog::Accepted ) 
  {
    /* tell our canvas that we want to print its */
    canvas_->print(aPrinter);
  }
}


//-------------------------------------------------------------
// slot responsible for current canvas as a bitmap
//-------------------------------------------------------------
void MainWindow::show_export_canvas_widget_()
{
  QString currentDirectory = QDir::currentPath();
  QString saveFileName = QFileDialog::getSaveFileName(this,
    tr("Export Canvas"), currentDirectory, 
    tr("Image Files (*.png *.jpg *.bmp *.ppm)"));

  if ( saveFileName.isEmpty() ) 
  {
    return;
  }

  /* extract file extension and make sure it corresponds to
   * a supported format */
  QFileInfo saveFileInfo(saveFileName);
  QString extension = saveFileInfo.completeSuffix();

  if ( extension != "png" && extension != "jpg" 
    && extension != "bmp" && extension != "ppm" )
  {
    QMessageBox::warning(this, tr("Warning"), 
      tr("Unknown file format ") + extension,
      QMessageBox::Ok);
    return;
  }

  /* instruct GraphCanvas to export its content */
  canvas_->export_bitmap(saveFileName, extension);
}


//-------------------------------------------------------------
// slot responsible for closing the application
//-------------------------------------------------------------
void MainWindow::exit_dvalin_()
{
  QMessageBox::StandardButton answer = QMessageBox::warning(this, 
      tr("Warning"), tr("Are you sure you want to quit?"),
      QMessageBox::Yes | QMessageBox::No);

  if ( answer == QMessageBox::Yes )
  {
    exit(0);
  }
}


//-------------------------------------------------------------
// slot responsible for starting the file loading progress bar
//-------------------------------------------------------------
void MainWindow::start_progress_bar_()
{
  fileLoadProgressBar_->setMinimum(0); 
  fileLoadProgressBar_->setMaximum(0); 
  fileLoadProgressBar_->setFormat(tr("Loading"));
}


//-------------------------------------------------------------
// slot responsible for starting the file loading progress bar
//-------------------------------------------------------------
void MainWindow::stop_progress_bar_()
{
  fileLoadProgressBar_->setMaximum(1); 
  fileLoadProgressBar_->reset(); 
}  


//-------------------------------------------------------------
// slot responsible for reloading the data of all files in
// the filequeue
//-------------------------------------------------------------
void MainWindow::reload_data_()
{
  ioHandler_->mark_filequeue_unread();
  ioHandler_->start();
}


//-------------------------------------------------------------
// slot responsible for opening a dialog to add labels to
// the graph
//-------------------------------------------------------------
void MainWindow::show_label_widget_()
{
  DataLabelWidget* labelWidget = 
    new DataLabelWidget(dataLabels_,this);
  labelWidget->setAttribute(Qt::WA_DeleteOnClose);
  labelWidget->show();
}


//-------------------------------------------------------------
// slot responsible for opening the settings dialog 
//-------------------------------------------------------------
void MainWindow::show_settings_widget_()
{
  /* create widget */
  SettingsWidget* settings = new SettingsWidget(settings_);
  settings->Init();
  settings->setAttribute(Qt::WA_DeleteOnClose);
  settings->show();
}


//-------------------------------------------------------------
// toggle visibility of a given data set
//-------------------------------------------------------------
void MainWindow::toggle_data_visibility_(bool state)
{
  /* retrieve plot properties for current data set */
  QString selectedDataName = get_active_data_name_();

  PlotPropertiesPtr plotProperties = 
    plotPropertiesContainer_[selectedDataName];

  plotProperties->set_visibility(state);
  redraw_and_rescale_canvas();
}




/**************************************************************
 *
 * PRIVATE MEMBER FUNCTIONS
 *
 **************************************************************/

//-------------------------------------------------------------
// create status bar
//-------------------------------------------------------------
void MainWindow::create_status_bar_()
{
  statusBar_ = new QStatusBar(this);
  statusBar_->setSizeGripEnabled(false);
 
  /* add welcome message widget */
  QString message = "This is <b>" + IDENTIFIER + "</b>      ";
  statusBarMessages_ = new QLabel(message);
  statusBarMessages_->setMinimumWidth(200);
  statusBar_->addWidget(statusBarMessages_,1);

  /* add mouse position widget */
  QString coordinateString = generate_coordinate_string(0, 0);
  currentCoordinateWidget_ = new QLabel(coordinateString);
  currentCoordinateWidget_->setMinimumWidth(200);
  currentCoordinateWidget_->setAlignment(Qt::AlignCenter);
  statusBar_->addPermanentWidget(currentCoordinateWidget_,0);

  /* add progress bar */
  fileLoadProgressBar_ = new QProgressBar(this); 
  fileLoadProgressBar_->setMinimumWidth(200);
  fileLoadProgressBar_->setMaximumWidth(200);
  fileLoadProgressBar_->setTextVisible(true); 
  fileLoadProgressBar_->reset(); 
  statusBar_->addPermanentWidget(fileLoadProgressBar_,0);

  /* add to main window */
  setStatusBar(statusBar_);
}


//-------------------------------------------------------------
// create menubar 
//-------------------------------------------------------------
void MainWindow::create_menu_bar_()
{
  menuBar_ = new QMenuBar(this);
  setMenuBar(menuBar_);
}


//-------------------------------------------------------------
// create toolbar
//-------------------------------------------------------------
void MainWindow::create_tool_bar_()
{
  QToolBar* toolBar = new QToolBar(this);
 
  QToolButton* openButton = new QToolButton(this);
  openButton->setIcon(QIcon(":/icons/fileopen.png"));
  openButton->setToolTip(tr("open data file"));
  toolBar->addWidget(openButton);
  connect(openButton,SIGNAL(clicked()),this,
      SLOT(show_open_new_file_widget_()));

  QToolButton* exportButton = new QToolButton(this);
  exportButton->setIcon(QIcon(":/icons/fileexport.png"));
  exportButton->setToolTip(tr("export canvas"));
  toolBar->addWidget(exportButton);
  connect(exportButton,SIGNAL(clicked()),this,
      SLOT(show_export_canvas_widget_()));
 
  QToolButton* printButton = new QToolButton(this);
  printButton->setIcon(QIcon(":/icons/fileprint.png"));
  printButton->setToolTip(tr("print canvas"));
  toolBar->addWidget(printButton);
  connect(printButton,SIGNAL(clicked()),this,
      SLOT(show_print_canvas_widget_()));

  toolBar->addSeparator();

  QToolButton* histogramButton = new QToolButton(this);
  histogramButton->setIcon(QIcon(":/icons/histogram.png"));
  histogramButton->setToolTip(tr("compute & show histogram"));
  toolBar->addWidget(histogramButton);
  connect(histogramButton,SIGNAL(clicked()),this,
      SLOT(show_histogram_selector()));
  
  QToolButton* labelButton = new QToolButton(this);
  labelButton->setIcon(QIcon(":/icons/labels.png"));
  labelButton->setToolTip(tr("edit graph labels"));
  toolBar->addWidget(labelButton);
  connect(labelButton,SIGNAL(clicked()),this,
      SLOT(show_label_widget_()));
  
  toolBar->addSeparator(); 

  QToolButton* zoomInButton = new QToolButton(this);
  zoomInButton->setIcon(QIcon(":/icons/viewmag+.png"));
  zoomInButton->setToolTip(tr("zoom in"));
  toolBar->addWidget(zoomInButton);
  connect(zoomInButton,SIGNAL(clicked()),canvas_,SLOT(zoom_in()));
  
  QToolButton* zoomOutButton = new QToolButton(this);
  zoomOutButton->setIcon(QIcon(":/icons/viewmag-.png"));
  zoomOutButton->setToolTip(tr("zoom out"));
  toolBar->addWidget(zoomOutButton);
  connect(zoomOutButton,SIGNAL(clicked()),canvas_,SLOT(zoom_out()));

  toolBar->addSeparator();

  QToolButton* resetButton = new QToolButton(this);
  resetButton->setIcon(QIcon(":/icons/gohome.png"));
  resetButton->setToolTip(tr("reset view"));
  toolBar->addWidget(resetButton);
  connect(resetButton,SIGNAL(clicked()),canvas_,SLOT(reset_view()));
  
  toolBar->addSeparator();
 
  QToolButton* leftMoveButton = new QToolButton(this);
  leftMoveButton->setIcon(QIcon(":/icons/left.png"));
  leftMoveButton->setToolTip(tr("move canvas left"));
  toolBar->addWidget(leftMoveButton);
  connect(leftMoveButton,SIGNAL(clicked()),canvas_,SLOT(pan_left()));
  
  QToolButton* rightMoveButton = new QToolButton(this);
  rightMoveButton->setIcon(QIcon(":/icons/right.png"));
  rightMoveButton->setToolTip(tr("move canvas right"));
  toolBar->addWidget(rightMoveButton);
  connect(rightMoveButton,SIGNAL(clicked()),canvas_,SLOT(pan_right()));

  QToolButton* upMoveButton = new QToolButton(this);
  upMoveButton->setIcon(QIcon(":/icons/up.png"));
  upMoveButton->setToolTip(tr("move canvas up"));
  toolBar->addWidget(upMoveButton);
  connect(upMoveButton,SIGNAL(clicked()),canvas_,SLOT(pan_up()));
  
  QToolButton* downMoveButton = new QToolButton(this);
  downMoveButton->setIcon(QIcon(":/icons/down.png"));
  downMoveButton->setToolTip(tr("move canvas down"));
  toolBar->addWidget(downMoveButton);
  connect(downMoveButton,SIGNAL(clicked()),canvas_,SLOT(pan_down()));
  
  addToolBar(toolBar);
}


//-------------------------------------------------------------
// populate menubar with entries
//-------------------------------------------------------------
void MainWindow::populate_menu_bar_()
{
  create_file_menu_();
  create_look_and_feel_menu_();
  create_transformations_menu_();
  create_help_menu_();
}


//-------------------------------------------------------------
// create file menu
//-------------------------------------------------------------
void MainWindow::create_file_menu_()
{
  QMenu* fileMenu = menuBar_->addMenu(tr("&File"));

  /* open */
  QAction* openAction = 
    new QAction(QIcon(":/icons/fileopen.png"),tr("&Open"), this);
  fileMenu->addAction(openAction);
  openAction->setShortcut(tr("Ctrl+O"));
  connect(openAction, SIGNAL(triggered()), this,
      SLOT(show_open_new_file_widget_()));

  fileMenu->addSeparator();

  /* export */
  QAction* exportAction = 
    new QAction(QIcon(":/icons/fileexport.png"),tr("&Export"), this);
  fileMenu->addAction(exportAction);
  exportAction->setShortcut(tr("Ctrl+E"));
  connect(exportAction, SIGNAL(triggered()), this, 
      SLOT(show_export_canvas_widget_()));

  /* print */
  QAction* printAction = 
    new QAction(QIcon(":/icons/fileprint.png"),tr("&Print"), this);
  fileMenu->addAction(printAction);
  printAction->setShortcut(tr("Ctrl+P"));
  connect(printAction, SIGNAL(triggered()), this,
      SLOT(show_print_canvas_widget_()));

  fileMenu->addSeparator();

  /* exit */
  QAction* exitAction = 
    new QAction(QIcon(":/icons/exit.png"),tr("E&xit"), this);
  fileMenu->addAction(exitAction);
  exitAction->setShortcut(tr("Ctrl+X"));
  connect(exitAction, SIGNAL(triggered()), this,
      SLOT(exit_dvalin_()));
}


//-------------------------------------------------------------
// create look and feel menu
//-------------------------------------------------------------
void MainWindow::create_look_and_feel_menu_()
{
  QMenu* lookAndFeelMenu = menuBar_->addMenu(tr("&Look+Feel"));

  /* action for adding add labels */
  QAction* addLabelAction = new QAction(
    QIcon(":/icons/labels.png"),tr("&Edit labels"), this);
  addLabelAction->setShortcut(tr("Ctrl+L"));
  lookAndFeelMenu->addAction(addLabelAction);
  connect(addLabelAction,SIGNAL(triggered()), this,
      SLOT(show_label_widget_()));

  /* action for reloading data */
  QAction* reloadAction = new QAction(
      QIcon(":/icons/reload.png"),tr("&Reload"), this);
  reloadAction->setShortcut(tr("Ctrl+R"));
  lookAndFeelMenu->addAction(reloadAction);
  connect(reloadAction, SIGNAL(triggered()), this, 
      SLOT(reload_data_()));

  lookAndFeelMenu->addSeparator();

  /* action for changing sensitivity of data selection
   * for popup window */
  QAction* settingsAction = new QAction(
      QIcon(":/icons/kcontrol.png"),tr("&Settings"), this);
  settingsAction->setShortcut(tr("Ctrl+S"));
  lookAndFeelMenu->addAction(settingsAction);
  connect(settingsAction,SIGNAL(triggered()), this,
      SLOT(show_settings_widget_()));
}
  

//-------------------------------------------------------------
// create transformations menu
//-------------------------------------------------------------
void MainWindow::create_transformations_menu_()
{
  QMenu* transformationsMenu = menuBar_->addMenu(
      tr("&Transformations"));

  /* histogram transformation */
  QAction* histogramAction = 
    new QAction(QIcon(":/icons/histogram.png"),tr("Histogram"), this);
  histogramAction->setShortcut(tr("Ctrl+H"));
  transformationsMenu->addAction(histogramAction);
  connect(histogramAction,SIGNAL(triggered()), this,
      SLOT(show_histogram_selector()));
}


//-------------------------------------------------------------
// create help menu
//-------------------------------------------------------------
void MainWindow::create_help_menu_()
{
  QMenu* helpMenu = menuBar_->addMenu(tr("&Help"));

  /* add entries */
  QAction* aboutAction = new QAction(tr("&About"),this);
  helpMenu->addAction(aboutAction);
  connect(aboutAction, SIGNAL(triggered()), this,
      SLOT(show_info_()));

  QAction* aboutQtAction = new QAction(tr("About Qt"),this);
  helpMenu->addAction(aboutQtAction);
  connect(aboutQtAction, SIGNAL(triggered()), this, 
      SLOT(show_about_qt_info_()));
}

  
//-------------------------------------------------------------
// create QSplitter used as central widget
//-------------------------------------------------------------
void MainWindow::create_main_splitter_()
{
  mainSplitter_ = new QSplitter(this);
  mainSplitter_->setMinimumSize(SPLITTER_MIN_WIDTH,SPLITTER_MIN_HEIGHT);
}


//-------------------------------------------------------------
// create QComboBox for selecting the currently active 
// data set (for manipulation) and deleting it
//-------------------------------------------------------------
void MainWindow::create_data_selector_widget_()
{
  /* selector widget */
  dataSelector_ = new DataSelectorWidget(this);
  dataSelector_->Init();

  QVBoxLayout* selectorLayout = new QVBoxLayout;
  selectorLayout->setContentsMargins(2,2,2,2);
  selectorLayout->addWidget(dataSelector_);

  dataSelectorGrouper_ = new QGroupBox(tr("data selector"));
  dataSelectorGrouper_->setLayout(selectorLayout);

  /* sync dataSelector_ with the dataControl_ widget and
   * connect buttons */
  connect(dataSelector_,SIGNAL(selected_data_item_changed(int)),
      dataControl_,SLOT(setCurrentIndex(int)));
}


//-------------------------------------------------------------
// create QTabWidget organizing all tabs used for displaying
// per data file properties
//-------------------------------------------------------------
void MainWindow::create_data_property_widget_()
{
  dataControl_ = new QStackedWidget(this);
}


//-------------------------------------------------------------
// create QFrame that is used as the canvas for plotting
// the graphs
//-------------------------------------------------------------
void MainWindow::create_canvas_()
{
  canvas_ = new GraphCanvas(this, 0, 0, CANVAS_MIN_WIDTH,
    CANVAS_MIN_HEIGHT, dataContainer_, plotPropertiesContainer_,
    activeData_, dataLabels_, settings_);
  canvas_->Init();
}


//------------------------------------------------------------
// add all widgets to central widget and adjust the widget
// dimensions
//------------------------------------------------------------
void MainWindow::add_widgets_to_splitter_()
{
  mainSplitter_->addWidget(canvas_);

  /* put dataControl_ stacked widget in a GroupBox to label
   * it nicely */
  QGroupBox* dataControlGrouper = new QGroupBox(
      tr("data && plot properties"));
  QVBoxLayout* dataControlLayout = new QVBoxLayout;
  dataControlLayout->addWidget(dataControl_);
  dataControlGrouper->setLayout(dataControlLayout);

  /* add widgets on the right hand side widget panel */
  QGroupBox* rightHandSideWidgets = new QGroupBox;
  QVBoxLayout* rightLayout = new QVBoxLayout;
  rightLayout->addWidget(dataSelectorGrouper_);
  rightLayout->addWidget(dataControlGrouper);
  rightLayout->addStretch(1);
  rightHandSideWidgets->setLayout(rightLayout);
  mainSplitter_->addWidget(rightHandSideWidgets);

  /* adjust dimensions */
  QList<int> widgetSizes;
  widgetSizes.append(CANVAS_MIN_WIDTH);
  widgetSizes.append(TAB_WIDGET_MIN_WIDTH);
  mainSplitter_->setSizes(widgetSizes);
  mainSplitter_->setStretchFactor(0,1);
}


//------------------------------------------------------------
// initializes the plot properties for a new data set 
//------------------------------------------------------------
PlotPropertiesPtr MainWindow::initialize_plot_properties_(
    QString aName)
{
    PlotPropertiesPtr aProperty =
      PlotPropertiesPtr( new PlotProperties);
    aProperty->Init();
    plotPropertiesContainer_[aName] = aProperty;

    return aProperty;
}


//------------------------------------------------------------
// display the properties for data 
//------------------------------------------------------------
void MainWindow::add_data_property_tab_(
    QString dataName, DataObjectPtr theData,
    PlotPropertiesPtr plotProperties) 
{
  /* add tab to display graph properties */
  DataPropertyWidget* aPropertyWidget = 
    new DataPropertyWidget(this, dataName, theData, 
      plotProperties);
  aPropertyWidget->Init();
  
  int widgetId = dataControl_->addWidget(aPropertyWidget);
  dataControl_->setCurrentIndex(widgetId);
}


//------------------------------------------------------------
// display the properties for histogram
//------------------------------------------------------------
void MainWindow::add_histogram_property_tab_(
    const QString& dataName, const QString& histogramName, 
    DataObjectPtr histogramData, PlotPropertiesPtr plotProperties, 
    int numBins, bool normalize) 
{
  /* add tab to display graph properties */
  HistogramPropertyWidget* aPropertyWidget = 
    new HistogramPropertyWidget(this, histogramName, histogramData, 
        plotProperties, normalize);
  aPropertyWidget->Init(numBins, dataName);

  int widgetId = dataControl_->addWidget(aPropertyWidget);
  dataControl_->setCurrentIndex(widgetId);
}


//--------------------------------------------------------------
// update property tabs to pick up new data and property
// values, e.g. after a reload
//--------------------------------------------------------------
void MainWindow::update_individual_property_tab_(
    const QString& name) 
{
  BasePropertyWidget* selectedSet = get_data_property_widget_(name);

  /* this indicates trouble; log a message for now */
  if ( selectedSet == NULL ) 
  {
    std::cout << "Error: Failed to update properties for "
              << "data set " << name.toStdString() << std::endl;
    return;
  }

  DataObjectPtr newData = dataContainer_[name];
  selectedSet->update_widget(newData);
}


//--------------------------------------------------------------
// return a pointer to the DataPropertyWidget of a certain
// name in dataControl_
//--------------------------------------------------------------
BasePropertyWidget* MainWindow::get_data_property_widget_(
    const QString& name)
{
  int numDataSets = dataControl_->count();
  int counter = 0;
  BasePropertyWidget* selectedSet = NULL;
  while( counter < numDataSets)
  {
    selectedSet = qobject_cast<BasePropertyWidget*>(
        dataControl_->widget(counter));
 
    if ( selectedSet->get_name() == name )
    {
      return selectedSet;
    }

    counter++;
  }

  /* NULL indicates that we failed to retrieve the widget */
  return NULL;
}


//--------------------------------------------------------------
// set up threaded I/O handler
//--------------------------------------------------------------
void MainWindow::generate_ioHandler_()
{
  ioHandler_ = new IO(this, dataContainer_);
  connect(ioHandler_, SIGNAL(finished()), this, 
      SLOT(add_data_to_gui_()));
  connect(ioHandler_, SIGNAL(finished()), this, 
      SLOT(stop_progress_bar_()));
  connect(ioHandler_, SIGNAL(started()), this, 
      SLOT(start_progress_bar_()));
}


//----------------------------------------------------------------
// go through the DataContainer and add all data sets from
// missing from plotPropertiesContainer_; we use a mutex to
// make sure not more than one thread at a time does this 
//----------------------------------------------------------------
void MainWindow::add_data_to_gui_()
{
  /* only one QThread at a time during updating of the GUI */
  QMutexLocker locker(&mutex_);

  statusBarMessages_->setText(tr("ready :)"));  

  FOREACH(const DataContainerItem& item, dataContainer_)
  {
    QString dataName(item.first);
    DataObjectPtr theData = item.second;
  
    if ( std::find(activeData_.begin(), activeData_.end(), dataName) 
          == activeData_.end() )
    {
      PlotPropertiesPtr plotPtr = 
        initialize_plot_properties_(dataName);
      add_data_property_tab_(dataName, theData, plotPtr);

      /* all initialization done, make data active */
      add_to_active_data_(dataName);
      add_to_data_label_manager_(dataName);
      add_to_data_selector_(dataName);
    }
    else
    {
      update_individual_property_tab_(dataName);
    }
  }

  redraw_and_rescale_canvas();
}


//-----------------------------------------------------------------
// add the named data to the list of active data sets
//-----------------------------------------------------------------
void MainWindow::add_to_active_data_(QString dataName)
{
  activeData_.push_back(dataName);
}


//-----------------------------------------------------------------
// delete the named data from the list of active data sets
//-----------------------------------------------------------------
void MainWindow::remove_from_active_data_(QString dataName)
{
  activeData_.erase(remove(activeData_.begin(),activeData_.end(),
    dataName),activeData_.end());
}


//-----------------------------------------------------------------
// add the named data set to the DataLabelManager
//-----------------------------------------------------------------
void MainWindow::add_to_data_label_manager_(QString dataName)
{
  dataLabels_.add_data_set(dataName);
}


//-----------------------------------------------------------------
// delete the named data from the DataLabelManager
//-----------------------------------------------------------------
void MainWindow::remove_from_data_label_manager_(QString dataName)
{
  dataLabels_.remove_data_set(dataName);
}


//-----------------------------------------------------------------
// add the named data from the dataSelector_ using the default
// graph color
//-----------------------------------------------------------------
void MainWindow::add_to_data_selector_(QString dataName)
{
  const QColor& graphColor = get_graph_color_(dataName);
  dataSelector_->add_data_item(dataName, graphColor);
}


//-----------------------------------------------------------------
// return a pointer to the widget for the currently active data set
//-----------------------------------------------------------------
QWidget* MainWindow::get_active_data_widget_()
{
  return dataControl_->currentWidget();
}


//-----------------------------------------------------------------
// return the name of the currently selected active data set
//-----------------------------------------------------------------
QString MainWindow::get_active_data_name_()
{
   QWidget* theWidget = get_active_data_widget_();
   DataPropertyWidget* theData = 
     qobject_cast<DataPropertyWidget*>(theWidget);
   return theData->get_name();
}


//-----------------------------------------------------------------
// return the current graph color of the named data set
//-----------------------------------------------------------------
const QColor& MainWindow::get_graph_color_(QString name) 
{
  return plotPropertiesContainer_[name]->get_color();
}

  
//----------------------------------------------------------------
// add a newly created histogram to the main gui
//----------------------------------------------------------------
void MainWindow::add_histogram_(const QString& dataName, 
    DataObjectPtr histogramData, int numBins, bool normalize)
{
  /* add histogram data to data container */
  QString histogramName = generate_histogram_name(dataName,
      histogramCounter_++);
  dataContainer_[histogramName] = histogramData;

  /* initialize the plot properties */
  PlotPropertiesPtr plotPtr = 
    initialize_plot_properties_(histogramName);
  plotPtr->set_penStyle("line");

  /* all initialization done, make data active */
  add_histogram_property_tab_(dataName, histogramName, 
      histogramData, plotPtr, numBins, normalize);
  add_to_active_data_(histogramName);
  add_to_data_label_manager_(histogramName);
  add_to_data_selector_(histogramName);

  redraw_and_rescale_canvas();
}

